// Copyright (c) 2006, Gregory Todd <greg@oddsim.com>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
// 
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

using System;
using System.Collections.Generic;
using System.Text;

namespace Othello.Engine
{
    public class Board
    {
        public const int _BLACK = 1;
        public const int _WHITE = -1;
        public const int _EMPTY = 0;
        public const int _SIZE = 8;

        //public ulong BlackBoard
        //{
        //    get
        //    {
        //        ulong value = 0;
        //        for (int x = 0; x < _SIZE; x++)
        //        {
        //            for (int y = 0; y < _SIZE; y++)
        //            {
        //                value *= 2;
        //                if (blackBoard[x, y])
        //                    value += 1;
        //            }
        //        }
        //        return value;
        //    }
        //    set
        //    {
        //        for (int x = _SIZE - 1; x >= 0; x--)
        //        {
        //            for (int y = _SIZE - 1; y >= 0; y--)
        //            {
        //                if (value % 2 == 1)
        //                    blackBoard[x, y] = true;
        //                else
        //                    blackBoard[x, y] = false;
        //                value /= 2;
        //            }
        //        }
        //    }
        //}
        //public ulong WhiteBoard
        //{
        //    get
        //    {
        //        ulong value = 0;
        //        for (int x = 0; x < _SIZE; x++)
        //        {
        //            for (int y = 0; y < _SIZE; y++)
        //            {
        //                value *= 2;
        //                if (whiteBoard[x, y])
        //                    value += 1;
        //            }
        //        }
        //        return value;
        //    }
        //    set
        //    {
        //        for (int x = _SIZE - 1; x >= 0; x--)
        //        {
        //            for (int y = _SIZE - 1; y >= 0; y--)
        //            {
        //                if (value % 2 == 1)
        //                    whiteBoard[x, y] = true;
        //                else
        //                    whiteBoard[x, y] = false;
        //                value /= 2;
        //            }
        //        }
        //    }
        //}

        //public bool Same(Board other)
        //{
        //    for (int x = 0; x < _SIZE; x++)
        //    {
        //        for (int y = 0; y < _SIZE; y++)
        //        {
        //            if (this.blackBoard[x, y] != other.blackBoard[x, y])
        //                return false;
        //            if (this.whiteBoard[x, y] != other.whiteBoard[x, y])
        //                return false;
        //        }
        //    }
        //    return true;
        //}

        public bool[,] blackBoard = new bool[_SIZE, _SIZE];
        public bool[,] whiteBoard = new bool[_SIZE, _SIZE];

        #region Space Operations

        /// <summary>
        /// Tests if a space is in-bounds
        /// </summary>
        /// <param name="x">column</param>
        /// <param name="y">row</param>
        /// <returns>true if the space is in-bounds</returns>
        public bool SpaceValid(int x, int y)
        {
            if (x < 0 || y < 0 || x >= _SIZE || y >= _SIZE)
                return false;
            else
                return true;
        }

        /// <summary>
        /// Gets the color of the playerColor occupying the space
        /// </summary>
        /// <param name="x">column</param>
        /// <param name="y">row</param>
        /// <returns>integer representing the color of the playerColor occupying the space</returns>
        public int SpaceColor(int x, int y)
        {
            if (blackBoard[x, y])
                return _BLACK;
            else if (whiteBoard[x, y])
                return _WHITE;
            else
                return _EMPTY;
        }

        /// <summary>
        /// Sets the space to the indicated currentValue
        /// </summary>
        /// <param name="x">column</param>
        /// <param name="y">row</param>
        /// <param name="playerColor">currentValue of the space</param>
        public void SpaceSet(int x, int y, int playerColor)
        {
            if (playerColor == _BLACK)
            {
                blackBoard[x, y] = true;
                whiteBoard[x, y] = false;
            }
            else
            {
                blackBoard[x, y] = false;
                whiteBoard[x, y] = true;
            }
        }

        #endregion

        /// <summary>
        /// Make a copy of the board
        /// </summary>
        /// <returns>Copy of the board</returns>
        public Board Copy()
        {
            Board copy = new Board();
            copy.blackBoard = this.blackBoard.Clone() as bool[,];
            copy.whiteBoard = this.whiteBoard.Clone() as bool[,];
            return copy;
        }

        #region Scoring

        /// <summary>
        /// Gets the number of points the playerColor has
        /// </summary>
        /// <param name="playerColor"></param>
        /// <returns></returns>
        public int Points(int playerColor)
        {
            int points = 0;

            for (int x = 0; x < _SIZE; x++)
                for (int y = 0; y < _SIZE; y++)
                    if (SpaceColor(x, y) == playerColor)
                        points++;

            return points;
        }

        public int StaticEvaluator(int playerColor)
        {
            int score = Points(playerColor) - Points(-playerColor);

            // If the game is over
            if (GameOver())
            {
                // if player has won
                if (score > 0)
                    return 100;
                // if player has lost (or tied)
                else
                    return -100;
            }

            // if game isn't over, return relative advatage
            return score;
        }

        #endregion

        public List<Move> MoveGenerator(int player)
        {
            List<Move> moves = new List<Move>();

            for (int x = 0; x < _SIZE; x++)
            {
                for (int y = 0; y < _SIZE; y++)
                {
                    int value = MoveValue(x, y, player);
                    if (value > 0)
                        moves.Add(new Move(x, y, value));
                }
            }
            return moves;
        }
        public bool CanPlay(int player)
        {
            int x;
            int y;

            for (x = 0; x < _SIZE; x++)
            {
                for (y = 0; y < _SIZE; y++)
                {
                    if (MoveValue(x, y, player) > 0)
                        return true;
                }
            }

            return false;
        }
        public bool GameOver()
        {
            if (CanPlay(_BLACK) || CanPlay(_WHITE))
                return false;
            else
                return true;
        }
        public bool CanMove(int x, int y, int player)
        {
            if (MoveValue(x, y, player) > 0)
                return true;
            else
                return false;
        }

        public int MoveValue(int x, int y, int player)
        {
            //If the space is occupied, return 0
            if (SpaceColor(x, y) != _EMPTY)
                return 0;

            int value = 0;
            int xDirection; //Horizontal direction
            int yDirection; //Vertical direction
            int distance; //Distance
            int xTemp;
            int yTemp;

            for (xDirection = -1; xDirection <= 1; xDirection++)
            {
                for (yDirection = -1; yDirection <= 1; yDirection++)
                {
                    if (!(xDirection == 0 && yDirection == 0))
                    {
                        distance = 1;
                        xTemp = x + xDirection;
                        yTemp = y + yDirection;
                        while (SpaceValid(xTemp, yTemp) && SpaceColor(xTemp, yTemp) == -player)
                        {
                            distance++;
                            xTemp += xDirection;
                            yTemp += yDirection;
                        }
                        if (distance > 1 && SpaceValid(xTemp, yTemp) && SpaceColor(xTemp, yTemp) == player)
                            value += distance - 1;
                    }
                }
            }

            return value;
        }
        public int Move(int x, int y, int player)
        {
            // Place the piece at the selected location
            SpaceSet(x, y, player);

            int value = 0;
            int xDirection; //Horizontal direction
            int yDirection; //Vertical direction
            int distance; //Distance
            int xTemp;
            int yTemp;

            for (xDirection = -1; xDirection <= 1; xDirection++)
            {
                for (yDirection = -1; yDirection <= 1; yDirection++)
                {
                    if (!(xDirection == 0 && yDirection == 0))
                    {
                        distance = 1;
                        xTemp = x + xDirection;
                        yTemp = y + yDirection;
                        while (SpaceValid(xTemp, yTemp) && SpaceColor(xTemp, yTemp) == -player)
                        {
                            distance++;
                            xTemp += xDirection;
                            yTemp += yDirection;
                        }
                        if (distance > 1 && SpaceValid(xTemp, yTemp) && SpaceColor(xTemp, yTemp) == player)
                        {
                            value += distance - 1;

                            do
                            {
                                SpaceSet(xTemp, yTemp, player);
                                distance--;
                                xTemp -= xDirection;
                                yTemp -= yDirection;
                            } while (distance > 0);
                        }
                    }
                }
            }

            return value;
        }

        public static Board Empty()
        {
            Board board = new Board();
            board.blackBoard.Initialize();
            board.whiteBoard.Initialize();
            return board;
        }
        public static Board Start()
        {
            Board board = new Board();
            board.blackBoard.Initialize();
            board.whiteBoard.Initialize();
            board.SpaceSet(3, 3, _WHITE);
            board.SpaceSet(4, 4, _WHITE);
            board.SpaceSet(3, 4, _BLACK);
            board.SpaceSet(4, 3, _BLACK);
            return board;
        }
    }
}
